""" This module contains the definitions of names, guards
"""
class Name(object):
    """ A base class for names in pi-graphs """
    def __init__(self):
        raise NotImplementedError("abstract class")
    
    def __eq__(self, other):
        """ Test for equality of names """
        return repr(self) == repr(other)
    
    def __ne__(self, other_name):
        """ Test for inequality of names """
        return repr(self) != repr(other_name)
    
    def __gt__(self, other):
        """ Test if a name is greater than another """
        return repr(self) > repr(other)
    
    def __lt__(self, other):
        """ Test if a name is lesser than another """
        return repr(self) < repr(other)
    
    def __ge__(self, other):
        """ Test if a name is greater than or equal to another """
        return repr(self) >= repr(other)
    
    def __le__(self, other):
        """ Test if a name is lesser than or equal to another """
        return repr(self) <= repr(other)
    
    def __hash__(self):
        """ Compute the hash value """
        return hash(repr(self))
    

class SyntacticName(Name):
    """ Name appears in the syntax of pi-graphs
    """
    def __init__(self, label):
        """ Each syntactic name has a label """
        self._label = label
        
    def getLabel(self):
        """ Get the label """
        return self._label
    
    def setLabel(self, label):
        """ Update the label """
        self._label = label
        
    def __str__(self):
        return self._label
    
    
class FreshName(Name):
    """ Name that is generated during the evolution of the graph
    """
    def __init__(self, idx):
        """ Each fresh name has an index, starts from 1 """
        self._idx = idx
        
    def getID(self):
        """ Get the index of a fresh name """
        return self._idx
    
    
class GlobalName(SyntacticName):
    """ Name, whose scope is inside the system
    """
    def __init__(self, label):
        SyntacticName.__init__(self, label)
        
    
class LocalName(SyntacticName):
    """ Name, whose scope is inside a replicator
    """
    def __init__(self, label, rname, thread=None):
        """ Each local name has a replicator name, a thread
        @param rname: name of replicator to which the name belongs
        @param thread: the thread to which the name belongs,
            thread != None means that it is assigned to a thread
        """
        SyntacticName.__init__(self, label)
        self._r = rname
        self._thrd = thread
    
    def rname(self):
        """ Get the name of the replicator """
        return self._r
    
    def thread(self):
        """ Get the thread to which the name belongs """
        return self._thrd
    
    def setThread(self, thread):
        """ Assign the name to a thread
        @return: a new local name corresponding to the thread
        """
        if isinstance(self, TResName):
            return TResName(self._label, self._r, thread)
        elif isinstance(self, VarName):
            return VarName(self._label, self._r, thread)
        else:
            raise Exception("name does not depends on threads")
    
    
class StaticFreshName(FreshName):
    """ Fresh name that will not be disposed at the end of a session
    """
    def __init__(self, idx):
        FreshName.__init__(self, idx)
        
    def __str__(self):
        return "s%d" % self._idx  # s = static
    
    def __repr__(self):
        return "s%d" % self._idx  # s = static
    
    
class DynamicFreshName(FreshName):
    """ Name that will be disposed at the end of a session
    """
    def __init__(self, idx):
        FreshName.__init__(self, idx)


class GResName(GlobalName):
    """ A graph restricted name
    """
    def __init__(self, label):
        GlobalName.__init__(self, label)
        
    def __repr__(self):
        return "nu_%s" % self._label


class FreeName(GlobalName):
    """ A free name
    """
    def __init__(self, label):
        GlobalName.__init__(self, label)
        
    def __repr__(self):
        return self._label
    

class TResName(LocalName):
    """ A thread restricted name
    """
    def __init__(self, label, rname, thread=None):
        LocalName.__init__(self, label, rname, thread)
        
    def __repr__(self):
        if self._thrd is None:
            return "nu_%s.%s" % (self._label, self._r)
        else:
            return "nu_%s.%s.%d" % (self._label, self._r, self._thrd)


class VarName(LocalName):
    ''' A variable name.'''
    def __init__(self, label, rname, thread=None):
        LocalName.__init__(self, label, rname, thread)
        
    def __repr__(self):
        if self._thrd is None:
            return "%s.%s" % (self._label, self._r)
        else:
            return "%s.%s.%d" % (self._label, self._r, self._thrd)
        

class InputName(DynamicFreshName):
    """ An fresh input name
    """
    def __init__(self, idx):
        FreshName.__init__(self, idx)
        
    def __str__(self):
        return "?%d" % self._idx  # prefix '?'
    
    def __repr__(self):
        return "?%d" % self._idx


class OutputName(DynamicFreshName):
    """ A fresh output name
    """
    def __init__(self, idx):
        FreshName.__init__(self, idx)
        
    def __str__(self):
        return "!%d" % self._idx  # prefix '!'
    
    def __repr__(self):
        return "!%d" % self._idx


class PrivateName(DynamicFreshName):
    """ A fresh private name
    """
    def __init__(self, idx):
        FreshName.__init__(self, idx)
        
    def __str__(self):
        return "nu%d" % self._idx
    
    def __repr__(self):
        return "nu%d" % self._idx
    

class Unknown(StaticFreshName):
    """ An unknown/undefined name.
    """
    def __init__(self):
        StaticFreshName.__init__(self, 0)
    
    def __str__(self):
        return "*"
    
    def __repr__(self):
        return "*"

# A special name indicate an undefined name.
BOTTOM = Unknown()    


class NameComparison:
    """ A comparison of two name
    """
    def __init__(self, name1, name2):
        """ A name comparison consists of two names
        """
        self._name1 = name1
        self._name2 = name2
    
    def firstname(self):
        """ Get the first name
        """
        return self._name1
    
    def secondname(self):
        """ Get the second name
        """
        return self._name2


class Match(NameComparison):
    """ A pair of equal names
    """
    def __init__(self, name1, name2):
        NameComparison.__init__(self, name1, name2)
        
    def __str__(self):
        return "[%s=%s]" % (self._name1, self._name2)
    
    def __repr__(self):
        return "[%r=%r]" % (self._name1, self._name2)


class Mismatch(NameComparison):
    """ A pair of unequal names
    """
    def __init__(self, name1, name2):
        NameComparison.__init__(self, name1, name2)
    
    def __str__(self):
        return "[%s!=%s]" % (self._name1, self._name2)
    
    def __repr__(self):
        return "[%r!=%r]" % (self._name1, self._name2)


class Guard:
    """ A list of matches/mismatches
    """
    def __init__(self, src=None):
        if src is None:
            self._comp = []
        else:
            self._comp=src
    
    def add(self, c):
        """ Add a name match/mismatch into list
        """
        self._comp.append(c)
        return self
    
    def comparison(self, i=None):
        """ Get a list of matches/mismatches
        """
        if i is None:
            return self._comp
        else:
            return self._comp[i]
    
    def __len__(self):
        """ Get number of name comparisons
        """
        return len(self._comp)
    
    def __getitem__(self, k):
        """ Get a name comparison by index
        """
        if k < 0 or k >= len(self._comp):
            raise IndexError()
        return self._comp[k]
    
    def __str__(self):
        return "".join(str(c) for c in self._comp)

    def __repr__(self):
        return "".join(repr(c) for c in self._comp)